In this article, we will discuss some interesting problems on C language that can help students to brush up their C programming skills and help them prepare their C fundamentals for interviews.
1. gets() function
Question: There is a hidden problem with the following code. Can you detect it?
#include<stdio.h> int main(void) { char buff[10]; memset(buff,0,sizeof(buff)); gets(buff); printf("\n The buffer entered is [%s]\n",buff); return 0; }
Answer: The hidden problem with the code above is the use of the function gets(). This function accepts a string from stdin without checking the capacity of buffer in which it copies the value. This may well result in buffer overflow. The standard function fgets() is advisable to use in these cases.
2. strcpy() function
Question: Following is the code for very basic password protection. Can you break it without knowing the password?
#include<stdio.h> int main(int argc, char *argv[]) { int flag = 0; char passwd[10]; memset(passwd,0,sizeof(passwd)); strcpy(passwd, argv[1]); if(0 == strcmp("LinuxGeek", passwd)) { flag = 1; } if(flag) { printf("\n Password cracked \n"); } else { printf("\n Incorrect passwd \n"); } return 0; }
Answer: Yes. The authentication logic in above password protector code can be compromised by exploiting the loophole of strcpy() function. This function copies the password supplied by user to the ‘passwd’ buffer without checking whether the length of password supplied can be accommodated by the ‘passwd’ buffer or not. So if a user supplies a random password of such a length that causes buffer overflow and overwrites the memory location containing the default value ’0′ of the ‘flag’ variable then even if the password matching condition fails, the check of flag being non-zero becomes true and hence the password protection is breached.
For example :
$ ./psswd aaaaaaaaaaaaa Password cracked
So you can see that though the password supplied in the above example is not correct but still it breached the password security through buffer overflow.
To avoid these kind of problems the function strncpy() should be used.
Note from author : These days the compilers internally detect the possibility of stack smashing and so they store variables on stack in such a way that stack smashing becomes very difficult. In my case also, the gcc does this by default so I had to use the the compile option ‘-fno-stack-protector’ to reproduce the above scenario.
3. Return type of main()
Question: Will the following code compile? If yes, then is there any other problem with this code?
#include<stdio.h> void main(void) { char *ptr = (char*)malloc(10); if(NULL == ptr) { printf("\n Malloc failed \n"); return; } else { // Do some processing free(ptr); } return; }
Answer: The code will compile error free but with a warning (by most compilers) regarding the return type of main()function. Return type of main() should be ‘int’ rather than ‘void’. This is because the ‘int’ return type lets the program to return a status value. This becomes important especially when the program is being run as a part of a script which relies on the success of the program execution.
4. Memory Leak Question: Will the following code result in memory leak?
#include<stdio.h> void main(void) { char *ptr = (char*)malloc(10); if(NULL == ptr) { printf("\n Malloc failed \n"); return; } else { // Do some processing } return; }
Answer: Well, Though the above code is not freeing up the memory allocated to ‘ptr’ but still this would not cause a memory leak as after the processing is done the program exits. Since the program terminates so all the memory allocated by the program is automatically freed as part of cleanup. But if the above code was all inside a while loop then this would have caused serious memory leaks.
Note : If you want to know more on memory leaks and the tool that can detect memory leaks, read our article on Valgrind.
5. The free() function
Question: The following program seg-faults (crashes) when user supplies input as ‘freeze’ while it works fine with input ‘zebra’. Why?
#include<stdio.h> int main(int argc, char *argv[]) { char *ptr = (char*)malloc(10); if(NULL == ptr) { printf("\n Malloc failed \n"); return -1; } else if(argc == 1) { printf("\n Usage \n"); } else { memset(ptr, 0, 10); strncpy(ptr, argv[1], 9); while(*ptr != 'z') { if(*ptr == '') break; else ptr++; } if(*ptr == 'z') { printf("\n String contains 'z'\n"); // Do some more processing } free(ptr); } return 0; }
Answer: The problem here is that the code changes the address in ‘ptr’ (by incrementing the ‘ptr’) inside the while loop. Now when ‘zebra’ is supplied as input, the while loop terminates before executing even once and so the argument passed to free() is the same address as given by malloc(). But in case of ‘freeze’ the address held by ptr is updated inside the while loop and hence incorrect address is passed to free() which causes the seg-fault or crash.
6. atexit with _exit
Question: In the code below, the atexit() function is not being called. Can you tell why?
#include<stdio.h> void func(void) { printf("\n Cleanup function called \n"); return; } int main(void) { int i = 0; atexit(func); for(;i<0xffffff;i++); _exit(0); }
Answer: This behavior is due to the use of function _exit(). This function does not call the clean-up functions like atexit() etc. If atexit() is required to be called then exit() or ‘return’ should be used.
7. void* and C structures
Question: Can you design a function that can accept any type of argument and returns an integer? Also, is there a way in which more than one arguments can be passed to it?
Answer: A function that can accept any type of argument looks like : int func(void *ptr)
if more than one argument needs to be passed to this function then this function could be called with a structure object where-in the structure members can be populated with the arguments that need to be passed.
8. * and ++ operators
Question: What would be the output of the following code and why?
#include<stdio.h> int main(void) { char *ptr = "Linux"; printf("\n [%c] \n",*ptr++); printf("\n [%c] \n",*ptr); return 0; }
Answer: The output of the above would be :
[L]
[i] Since the priority of both ‘++’ and ‘*’ are same so processing of ‘*ptr++’ takes place from right to left. Going by this logic, ptr++ is evaluated first and then *ptr. So both these operations result in ‘L’. Now since a post fix ‘++’ was applied on ptr so the next printf() would print ‘i’.
9. Making changes in Code(or read-only) segment
Question: The following code seg-faults (crashes). Can you tell the reason why?
#include<stdio.h> int main(void) { char *ptr = "Linux"; *ptr = 'T'; printf("\n [%s] \n", ptr); return 0; }
Answer: This is because, through *ptr = ‘T’, the code is trying to change the first byte of the string ‘Linux’ kept in the code (or the read-only) segment in the memory. This operation is invalid and hence causes a seg-fault or a crash.
10. Process that changes its own name
Question: Can you write a program that changes its own name when run?
Answer: Following piece of code tries to do the required :
#include<stdio.h> int main(int argc, char *argv[]) { int i = 0; char buff[100]; memset(buff,0,sizeof(buff)); strncpy(buff, argv[0], sizeof(buff)); memset(argv[0],0,strlen(buff)); strncpy(argv[0], "NewName", 7); // Simulate a wait. Check the process // name at this point. for(;i<0xffffffff;i++); return 0; }
11. Returning address of local variable
Question: Is there any problem with the following code?If yes, then how it can be rectified?
#include<stdio.h> int* inc(int val) { int a = val; a++; return &a; } int main(void) { int a = 10; int *val = inc(a); printf("\n Incremented value is equal to [%d] \n", *val); return 0; }
Answer: Though the above program may run perfectly fine at times but there is a serious loophole in the function ‘inc()’. This function returns the address of a local variable. Since the life time of this local variable is that of the function ‘inc()’ so after inc() is done with its processing, using the address of its local variable can cause undesired results. This can be avoided by passing the address of variable ‘a’ from main() and then inside changes can be made to the value kept at this address.
12. Processing printf() arguments
Question: What would be the output of the following code?
#include<stdio.h> int main(void) { int a = 10, b = 20, c = 30; printf("\n %d..%d..%d \n", a+b+c, (b = b*2), (c = c*2)); return 0; }
Answer: The output of the above code would be :
110..40..60 This is because the arguments to the function are processed from right to left but are printed from left to right.